package com.godaiyu.dubbo.monitor;

import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;


/**
 * @author godai
 * @date 2021/2/24 21:53
 * @description
 */
@Activate(group = {CommonConstants.CONSUMER})
public class TPMonitorFilter implements Filter {

    static Map<String, Map<Long, List<Long>>> splitTime = new HashMap<>();

    static {
        Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(
                () -> {
                    System.out.println("====================");

                    for (String k : splitTime.keySet()) {
                        long l = System.currentTimeMillis();
                        long timeSec = l / 1000;
                        Map<Long, List<Long>> longListMap = splitTime.get(k);
                        List<Long> tpData = new ArrayList<>();
                        for (Long timeSecK : new ArrayList<>(longListMap.keySet())) {
                            if (timeSec == timeSecK) {
                                continue;
                            } else if (timeSec - timeSecK > 60) {
                                longListMap.remove(timeSecK);
                                continue;
                            }
                            tpData.addAll(longListMap.get(timeSecK));
                        }

                        Collections.sort(tpData);
                        long tp90 = tp(tpData, 90);
                        long tp99 = tp(tpData, 99);
                        System.out.println(k + " " + timeSec + " count: "
                                + tpData.size()
                                + " TP90:" + tp90
                                + " TP99:" + tp99
                        );
                    }
                },
                0L, 5L, TimeUnit.SECONDS);
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        long startTime = System.currentTimeMillis();
        String k = invocation.getServiceName() + "." + invocation.getMethodName();
        try {
            // 执行方法
            return invoker.invoke(invocation);
        } finally {
            long curr = System.currentTimeMillis();
            if (!splitTime.containsKey(k) || splitTime.get(k) == null) {
                splitTime.put(k, new ConcurrentHashMap<>());
            }

            Long timeSec = curr / 1000;
            Map v = splitTime.get(k);
            if (!v.containsKey(timeSec) || v.get(timeSec) == null) {
                v.put(timeSec, Collections.synchronizedList(new ArrayList<>()));
            }
            splitTime.get(k).get(timeSec).add(curr - startTime);
        }
    }

    private static long tp(List<Long> times, int percent) {
        float percentF = (float) percent / 100;
        int index = (int) (percentF * times.size() - 1);
        return times.get(index);
    }

}
